package auth

import (
	"context"
	"encoding/base64"
	"encoding/json"
	"fmt"
	"slices"
	"strings"
	"time"
	"yuanxin-admin/internal/common/session"
	"yuanxin-admin/internal/model"
	"yuanxin-admin/internal/repository"

	"gitee.com/botaoshow/yuanxin/web/common/database"
	"gitee.com/botaoshow/yuanxin/web/common/redis"
	"gitee.com/botaoshow/yuanxin/web/response"
	"gitee.com/botaoshow/yuanxin/web/y"
	"github.com/gin-gonic/gin"
)

func InitAuth(r *gin.Engine) {
	var routes []string

	// 使用具体的上下文
	ctx := context.Background()

	// 先检查 repository 是否正确初始化
	if repository.SysURL == nil {
		y.Error("SysURL repository is not initialized")
		return
	}

	// 删除现有数据
	// 删除现有数据 - 正确的执行方式
	result := repository.SysURL.WithContext(ctx).UnderlyingDB().
		Exec("TRUNCATE TABLE sys_url")
	if result.Error != nil {
		y.Error(fmt.Sprintf("Failed to truncate sys_url table: %v", result.Error))
		return
	}

	var list []*model.SysURL
	for _, route := range r.Routes() {
		routes = append(routes, route.Method+" "+route.Path)
		bid := base64.URLEncoding.EncodeToString([]byte(route.Method + "<=>" + route.Path))
		list = append(list, &model.SysURL{
			ID:         bid,
			HTTPMethod: route.Method,
			HTTPURL:    route.Path,
			ClassName:  route.Handler,
			Method:     route.Handler[strings.LastIndex(route.Handler, ".")+1:],
			CreatedAt:  time.Now(),
		})
	}

	// 批量创建新数据
	if len(list) > 0 {
		err := repository.SysURL.WithContext(ctx).CreateInBatches(list, 100)
		if err != nil {
			y.Error(fmt.Sprintf("Failed to create URLs: %v", err))
			return
		}
	}
	//持久化到redis map存储 key:id value:role_code
	FlushCache(ctx)
	y.Info("enable auth with db")
}

func FlushCache(ctx context.Context) {
	FlushUrlRolesCache(ctx)
	FlushUrlWhitelistCache(ctx)
}
func FlushUrlRolesCache(ctx context.Context) {
	//sys_url_roles
	sql := `
SELECT
    sys_url.id,
    sys_role.role_code 
FROM
    sys_url
    LEFT JOIN sys_role_url ON sys_url.id = sys_role_url.url_id
    LEFT JOIN sys_role ON sys_role_url.role_id = sys_role.id 
ORDER BY
    sys_url.id,
    role_id
`
	var r []map[string]interface{}
	database.Db.Raw(sql).Scan(&r)
	rb := make(map[string][]string) // 初始化 map
	for _, item := range r {
		id := fmt.Sprintf("%v", item["id"])
		roleCode := fmt.Sprintf("%v", item["role_code"])
		rb[id] = append(rb[id], roleCode)
	}
	redis.Rdb.Del(ctx, "sys_url_roles")
	for k, v := range rb {
		vv, _ := json.Marshal(v)
		redis.Rdb.HSet(ctx, "sys_url_roles", k, string(vv))
	}
}
func FlushUrlWhitelistCache(ctx context.Context) {
	//sys_url_whitelist
	var r []map[string]interface{}
	database.Db.Raw("SELECT url_id,allow_anonymous from sys_url_whitelist").Scan(&r)
	rb := make(map[string]bool) // 初始化 map
	for _, item := range r {
		urlId := fmt.Sprintf("%v", item["url_id"])
		allowAnonymous := fmt.Sprintf("%v", item["allow_anonymous"])
		rb[urlId] = allowAnonymous == "0"
	}
	redis.Rdb.Del(ctx, "sys_url_whitelist")
	for k, v := range rb {
		vv := "true"
		if !v {
			vv = "false"
		}
		redis.Rdb.HSet(ctx, "sys_url_whitelist", k, vv)
	}
}

func HandleApi() gin.HandlerFunc {
	return func(c *gin.Context) {
		fullPath := c.FullPath()
		method := c.Request.Method
		apiBase64 := base64.URLEncoding.EncodeToString([]byte(method + "<=>" + fullPath))
		y.Info("请求地址:" + method + " " + fullPath)
		y.Info("请求地址:" + apiBase64)

		//是否登录
		userInfo := session.GetUserInfo(c)
		login := userInfo != nil
		var roles []string = make([]string, 0)
		if login {
			//获取用户角色
			roles = userInfo.Roles
			//是否包含admin
			//超级管理员放行所有接口
			if slices.Contains(roles, "admin") {
				c.Next()
				return
			}
		}
		// 判断接口是否在白名单内部 apiBase64 查询 sys_url_whitelist
		//redis 获取 sys_url_whitelist hash
		whiteList := redis.Rdb.HGetAll(c, "sys_url_whitelist").Val()
		if redis.Rdb.HExists(c, "sys_url_whitelist", apiBase64).Val() {
			y.Info("请求地址在白名单内")
			allowAnonymous := whiteList[apiBase64]
			if "true" == allowAnonymous {
				c.Next()
				return
			}
			if !login {
				// 401 未登录
				response.Resp(c, 401, "未认证，请登录后重试。", nil)
				return
			} else {
				c.Next()
				return
			}
		}

		if !redis.Rdb.HExists(c, "sys_url_roles", apiBase64).Val() {
			//url不在处理的url中 404 或者其他资源
			c.Next()
			return
		}
		ur := redis.Rdb.HGetAll(c, "sys_url_roles").Val()
		rolesStr := ur[apiBase64]
		var accessRoles []string = make([]string, 0)
		err := json.Unmarshal([]byte(rolesStr), &accessRoles)
		if err != nil {
			y.Error(err.Error())
			return
		}
		if len(accessRoles) == 0 {
			c.Next()
			return
		}

		if !login {
			// 401 未登录
			response.Resp(c, 401, "未认证，请登录后重试。", nil)
			return
		}
		flag := false
		if len(accessRoles) > 0 {
			for _, r := range accessRoles {
				if slices.Contains(roles, r) {
					flag = true
					break
				}
			}
		}
		if !flag {
			//403 无权限
			response.Resp(c, 403, "权限不足，拒绝访问。", nil)
			return
		}
		c.Next()
	}
}
